package fr.mywiki.business.entry.meta;

import java.rmi.RemoteException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import javax.ejb.ObjectNotFoundException;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;

import fr.mywiki.business.entry.EntryLite;
import fr.mywiki.model.ejbAdapter.utils.PersistenceException;
import fr.mywiki.model.queries.DatabaseEntryModelController;

/** Several methods dealing with entry models and entry-model links. */
public class EntryModelManager {
	public static final Long DEFAULT_ENTRY_MODEL_ID = new Long(1);
	
	/**
	 * Loads and returns the default model (article).
	 */
	public static EntryModel getDefaultEntryModel() throws SQLException,
			ModelException, JDOMException {
		return DatabaseEntryModelController.getModel(DEFAULT_ENTRY_MODEL_ID);
	}
	
	/**
	 * Creates a new model in the database.
	 * 
	 * @param name
	 *          the name of the model.
	 * @param properties
	 *          a not null <code>Collection</code> of <code>Property</code>
	 *          objects.
	 * @return a <code>EntryModel</code> object.
	 */
	public static EntryModel createModel(String name, Collection properties) throws RemoteException,
			PersistenceException, ObjectNotFoundException, ModelException {
		EntryModel model = createModel();

		model.setName(name);

		Iterator itProps = properties.iterator();
		while(itProps.hasNext()) {
			Property prop = (Property) itProps.next();
			model.addProperty(prop);
		}

		model.update();

		return model;
	}
	
	/**
	 * Modifies the given <code>EntryModel</code> object and updates it into the
	 * database.
	 * 
	 * @param model
	 *          the <code>EntryModel</code> object.
	 * @param name
	 *          the new name of the model.
	 */
	public static void modifyModel(EntryModel model, String name, Collection properties)
			throws ObjectNotFoundException, RemoteException, ModelException {
		model.setName(name);
		
		model.removeAllProperties();
		Iterator itProps = properties.iterator();
		while(itProps.hasNext())
			model.addProperty((Property) itProps.next());
		
		model.update();
	}
	
	/**
	 * Creates a new model in the database.
	 * 
	 * @return the created <code>EntryModel</code> object.
	 */
	public static EntryModel createModel() throws PersistenceException,RemoteException {
		try {
			return DatabaseEntryModelController.createModel();
		} catch (SQLException e) {
			throw new RemoteException(e.getMessage());
		}
	}

	/**
	 * Returns true if a model having this name exists.
	 * @param name the name of the model.
	 * @return true iif a <code>EntryModel</code> object having the specified name
	 */
	public static boolean existsModel(String name) throws SQLException {
		return DatabaseEntryModelController.existsModel(name);
	}
	
	/**
	 * Loads an <code>EntryModel</code> from the database.
	 * 
	 * @param id
	 *          the identifier of the model.
	 * @return a <code>EntryModel</code> object and associated properties.
	 */
	public static EntryModel findModelById(Long id) throws RemoteException {
		try {
			return DatabaseEntryModelController.getModel(id);
		} catch (SQLException e) {
			throw new RemoteException(e.getMessage());
		} catch (ModelException e) {
			throw new RemoteException(e.getMessage());
		} catch (JDOMException e) {
			throw new RemoteException(e.getMessage());
		}
	}
	
	/**
	 * Loads a <code>Collection</code> of <code>EntryModel</code> objects from
	 * the database.
	 * 
	 * @param name
	 *          the name of the model.
	 * @return a <code>Collection</code> of <code>EntryModel</code> objects
	 *         with associated properties.
	 * @throws JDOMException 
	 */
	public static Collection findModelsByName(String name) throws SQLException, ModelException, JDOMException {
		return DatabaseEntryModelController.getModels(name);
	}
	
	/**
	 * Loads all the <code>EntryModel</code> objects from the database.
	 * 
	 * @return a <code>Collection</code> of <code>EntryModel</code> objects with associated properties.
	 */
	public static Collection findAllModels() throws RemoteException {
		try {
			return DatabaseEntryModelController.findAllModels();
		} catch (SQLException e) {
			throw new RemoteException(e.getMessage());
		} catch (ModelException e) {
			throw new RemoteException(e.getMessage());
		}catch (JDOMException e) {
			throw new RemoteException(e.getMessage());
		}
	}

	/**
	 * Updates the model into the database.
	 * 
	 * @param model
	 *          a <code>EntryModel</code> object.
	 */
	public static void updateModel(EntryModel model) throws RemoteException {
		try {
			DatabaseEntryModelController.updateModel(model);
		} catch (ModelException e) {
			throw new RemoteException(e.getMessage());
		} catch (SQLException e) {
			throw new RemoteException(e.getMessage());
		} catch (PersistenceException e) {
			throw new RemoteException(e.getMessage());
		}
	}

	/**
	 * Removes the model from the database.
	 * 
	 * @param model
	 *          a <code>EntryModel</code> object. 
	 */
	public static void removeModel(EntryModel model) throws RemoteException {
		try {
			DatabaseEntryModelController.removeModel(model);
		} catch (ModelException e) {
			throw new RemoteException(e.getMessage());
		} catch (SQLException e) {
			throw new RemoteException(e.getMessage());
		}
	}
	
	/**
	 * This method fills the description of the entry model given in parameter
	 * from the given Xml document containing all this information.
	 * 
	 * @param model
	 *          a <code>EntryModel</code> object, its description eventually
	 *          already filled (the new properties will be added - except if a new one
	 *          has the same name as an old one, in this case, it will replace
	 *          it).
	 * @param doc
	 *          a complete Xml document of the form <entrymodel/>.
	 */
	public static void fillModelDescriptionFromXmlDocument(EntryModel model,
			Document doc) throws ModelException {
		Element root = doc.getRootElement(); // must be <entrymodel/>

		Iterator itCh = root.getChildren().iterator();
		while (itCh.hasNext())
			model.addProperty(PropertyFactory.createProperty((Element) itCh.next()));
	}
	
	//***************************************************************/
	/* Methods dedicated to the management of the entry-model links */
	//***************************************************************/
	
	/**
	 * Returns the model linked to the entry given in parameter.
	 * 
	 * @param entry
	 *          a <code>EntryLite</code> object.
	 * @return a <code>EntryModel</code> object, null if no model is linked to
	 *         the entry.
	 */
	public static EntryModel getModel(EntryLite entry)
			throws SQLException, ModelException, JDOMException {
		ArrayList models = new ArrayList(DatabaseEntryModelController.getLinkedModels(entry));
		if(models.isEmpty())
			return null;
		return (EntryModel) models.get(0);
	}
	
	/**
	 * Returns the entries linked to the model given in parameter.
	 * 
	 * @param model a <code>EntryModel</code> object.
	 * @return a Collection of <code>EntryLite</code> objects.
	 */
	public static Collection getLinkedEntries(EntryModel model)
			throws RemoteException, SQLException {
		return DatabaseEntryModelController.getLinkedEntries(model);
	}
	
	/**
	 * Adds a link between the specified entry and model.
	 * 
	 * @param entry a <code>EntryLite</code> object.
	 * @param model a <code>EntryModel</code> object.
	 */
	public static void link(EntryLite entry, EntryModel model) throws SQLException {
		Collection models = new ArrayList();
		models.add(model);
		DatabaseEntryModelController.setLinkedModels(entry,models);
	}
	
	/**
	 * Sets the entries linked to a model. Removes all the linked entries and
	 * successively adds one.
	 * 
	 * @param model
	 *          a <code>EntryModel</code> object.
	 * @param entries
	 *          a Collection of <code>EntryLite</code> objects.
	 */
	public static void setLinkedEntries(EntryModel model, Collection entries)
			throws SQLException {
		DatabaseEntryModelController.setLinkedEntries(model,entries);
	}
	
	/**
	 * Removes the link between the specified model and entry. If the model linked
	 * to entry is model, then the set of models linked to entry is reinitialized.
	 * 
	 * @param entry
	 *          a <code>EntryLite</code> object.
	 * @param model
	 *          a <code>EntryModel</code> object.
	 */
	public static void removeLink(EntryLite entry, EntryModel model)
			throws SQLException, ModelException, JDOMException {
		if(model.equals(getModel(entry)))
				DatabaseEntryModelController.removeAllLinkedModels(entry);
	}
	
	/**
	 * Specifies if there is a entry-model link between the specified entry and
	 * model.
	 * 
	 * @param entry
	 *          a <code>EntryLite</code> object.
	 * @param model
	 *          a <code>EntryModel</code> object.
	 * @return true if <code>model</code> belongs to the list of models linked
	 *         to <code>entry</code>.
	 */
	public static boolean existsLink(EntryLite entry, EntryModel model)
			throws SQLException, ModelException, JDOMException {
		return DatabaseEntryModelController.existsLink(entry,model);
	}
	
	/**
	 * Removes all the links between the specified entry and models.
	 * 
	 * @param entry
	 *          a <code>EntryLite</code> object.
	 */
	public static void removeAllLinkedModels(EntryLite entry) throws SQLException {
		DatabaseEntryModelController.removeAllLinkedModels(entry);
	}

	/**
	 * Removes all the links between the specified model and entries.
	 * 
	 * @param model
	 *          a <code>EntryModel</code> object.
	 */
	public static void removeAllLinkedEntries(EntryModel model)
			throws SQLException {
		DatabaseEntryModelController.removeAllLinkedEntries(model);
	}
}
